from turbogears import widgets

from mnemoply import reports
from mnemoply.utils import IdsValidator, escape, getMondayBeforeThat
from datetime import timedelta, datetime
from mnemoply.common_knowledge import day_captions
from mnemoply.model import Environment, GroupLesson, Holiday

_grid_step = 5
_start_hour = 9
_end_hour = 22

def _round_hm(hour, minute):
    x = (60 * hour + minute) / _grid_step * _grid_step
    hour, minute = x / 60, x % 60

    return hour, minute

def calc_period_minutes(academic_hours):
    if academic_hours > 1:
        return 10 + 45 * academic_hours
    else:
        return 45 * academic_hours

def normalize_hm_boundaries(hm):
    if hm[0] < _start_hour:
        hm[0] = (_start_hour, 0)
    if hm[0] > _end_hour:
        hm[0] = (_end_hour, 0)
    return hm

def _isLessonRemoved(lesson, removed):
    result = False
    for rem in removed:
        if lesson.group == rem[1]:
            result = True
            break

    return result

def generate_timetable_commands(classroom, starttimetable_row, params, errors):
    timetable_date = params["timetable_date"]
    show_teacher = params["show_teacher"]
    show_dates = params["show_dates"]
    show_time = params["show_time"]
    show_place = params["show_place"]
    show_names = params["show_names"]
    show_unused = params["show_unused"]

    start_date = getMondayBeforeThat(timetable_date)
    end_date = start_date + timedelta(days = 6)
    date_format = "%d.%m.%Y"

    result = ''

    # Header
    result += """
        <set range="room_number">%s</set>
        <set range="office_name">%s</set>
        <set range="start_date">%s</set>
        <set range="end_date">%s</set>
    """ % (
            classroom.roomNumber,
            escape(classroom.office.name),
            start_date.strftime(date_format),
            end_date.strftime(date_format)
        )

    used_hours = range(_start_hour, _end_hour + 1)
    holidays = set(Holiday.getHolidays(int(Environment.getValue('academic_year')), start_date, end_date))
    # Returns list of tuples (date, group, classroom, startTimeHour, startTimeMinute, endTime)

    for day in xrange(0, 7):
        current_day = start_date + timedelta(day)
        result += """
        <set range="date%d">%s</set>
    """ % (
            day + 1,
            current_day.strftime(date_format)
        )

        lessons = set(classroom.getLessonsByDateTime(current_day, _start_hour, _end_hour))
        added = GroupLesson.getAdditionalDaysByClassroom(classroom, current_day)
        removed = GroupLesson.getRemovedDaysByClassroom(classroom, current_day)
        for lesson in lessons:
            if _isLessonRemoved(lesson, removed): # this lesson was manually removed
                continue
            if current_day in holidays: # if current_day is holiday, no planned lessons
                continue
            added.append((current_day, lesson.group) + lesson.getLessonInfo()) # but added lessons can be present
        added.sort(key= lambda x: x[3]*60+x[4])
        times = []
        for lesson in added:
            # lesson => (date, group, classroom, startTimeHour, startTimeMinute, endTime)
            # indices    0     1      2          3              4                5
            excel_col = chr(ord('A') + lesson[0].weekday() + 1)

            start = ((lesson[3] - _start_hour) * 60 + lesson[4]) / _grid_step
            end = ((lesson[5].hour - _start_hour) * 60 + lesson[5].minute) / _grid_step
            rows = end - start

            # checks for lesson duration
            if rows <= 0:
                rows = 1
            if rows < 6: # say the lesson cannot be shorter than 30 min
                errors.append("\n%s, #%s %s: %02d:%02d - %02d:%02d: %s" % (
                        classroom.office.name,
                        classroom.roomNumber,
                        lesson[1].name,
                        int(lesson[3]), int(lesson[4]),
                        int(lesson[5].hour), int(lesson[5].minute),
                        _("lesson too short")
                    )
                )

            # checks for lessons overlaying
            ok = True
            for t in times:
                if t[0] <= start <= t[1] or t[0] <= end <= t[1]:
                    errors.append("\n%s, #%s %s: %s - %02d:%02d - %02d:%02d: %s" % (
                        classroom.office.name,
                        classroom.roomNumber,
                        lesson[1].name,
                        day_captions[day + 1],
                        int(lesson[3]), int(lesson[4]),
                        int(lesson[5].hour), int(lesson[5].minute),
                        _("lesson s time is taken by other lesson")
                        )
                    )
                    ok = False
                    break
            if not ok: # don't add and don't output overlaid lessons
                continue
                
            times.append((start, end))

            # Fill used - for hiding empty lines later
            if not show_unused:
                end_hour = lesson[5].hour
                if lesson[5].minute > 0:
                    end_hour += 1
                for hour in xrange(lesson[3], end_hour):
                    if hour in used_hours:
                        used_hours.remove(hour)

            start_cell = "%s%s" % (excel_col, starttimetable_row + start)

            # Generates output information
            text = ""
            # 1. title: group or plan name
            if show_names == 1: # group title
                text += lesson[1].name[:-5] # remove academic year with leading '-'
            elif show_names == 2: # plan title
                text += lesson[1].initialPlan.abbr
            elif show_names == 3: # trend title
                text += lesson[1].initialPlan.trend.name
            else: # keep place for new cases to show title
                pass
            # 2. Teacher
            if show_teacher:
                text += "\n" + lesson[1].teacher.surname
            # 3. Number of students
            if show_place:
                text += " (%s)" % lesson[1].cacheStudents
            # 4. Work time of group
            if show_time:
                text += "\n%02d:%02d - %02d:%02d" % (
                    int(lesson[3]), int(lesson[4]),
                    int(lesson[5].hour), int(lesson[5].minute))
            # 5. Dates of group start/end
            if show_dates:
                text += "\n" +\
                    lesson[1].startDate.strftime(date_format) +\
                    " - " +\
                    lesson[1].endDate.strftime(date_format)

            result += """
                <merge start="%s" rows="%s" cols="1" />
                <set range="%s">%s</set>
            """ % (start_cell, rows, start_cell, escape(text))

    if not show_unused:
        # Hide unused hours
        rows_per_hour = 60 / _grid_step
        for hour in used_hours:
            start_row = starttimetable_row + (hour - _start_hour) * rows_per_hour
            end_row = start_row + rows_per_hour - 1
            result += "<hide_rows rows=\"A%d:A%d\" />\n" % (start_row, end_row)

    return result

def process_errors(errors):
    result = "<errors>"
    for i, err in enumerate(errors):
#    for i in xrange(0, len(errors)):
        result += '\n<set range="A%d">%s</set>' % (
            i + 1,
            escape(err)
        )
    result += "\n</errors>"
    return result

class TimetableReport(reports.RDSReport):
    name = _("Timetable")
    tag = "Timetable"
    report_type = reports.RDSReportType.EXCEL
    template_module = "mnemoply.templates.reports.timetable"

    fields = [
        widgets.HiddenField(name="ids", label=_("IDs"),
            validator=IdsValidator),
        widgets.HiddenField(name="timetable_date"),
        widgets.HiddenField(name="show_teacher"),
        widgets.HiddenField(name="show_dates"),
        widgets.HiddenField(name="show_time"),
        widgets.HiddenField(name="show_place"),
        widgets.HiddenField(name="show_names"),
        widgets.HiddenField(name="show_unused"),
    ]

    pass_functions = (
        generate_timetable_commands,
        process_errors,
    )

exposed = TimetableReport()
